package com.zzw.springboot.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zzw.springboot.bean.SysUser;
import com.zzw.springboot.service.SysUserService;
import com.zzw.springboot.util.RedisUtil;
import com.zzw.springboot.util.Result;
import com.zzw.springboot.annotation.Log;
import com.zzw.springboot.util.UserBehaviorAnalysisUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author 赵志伟
 * @version 1.0
 */
@SuppressWarnings({"all"})
//前后端分离, 前端发出请求, 后端返回json数据
@Api(tags = "用户管理")
@Slf4j
@RestController
@RequestMapping("/sysUser")
public class SysUserController {
    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserBehaviorAnalysisUtil behaviorUtil;

    // Redis中用户访问次数前缀
    private static final String USER_VIEW_COUNT_PREFIX = "user:view:count:";
    // Redis中热门用户列表键
    private static final String HOT_USERS_KEY = "user:hot";

    //编写方法, 数据展示
    //@GetMapping("/list")
    //public Result list() {
    //    List<SysUser> list = sysUserService.list();
    //    return Result.success(list);
    //}

    /**
     * @param pageNum  显示第几页, 默认为 1
     * @param pageSize 每页有多少条 默认为10
     * @return
     */
    //@GetMapping("listByPage")
    //public Result listByPage(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
    //                         @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
    //    //1.在javaweb中, 分页查询已经学过
    //    Page<SysUser> page = sysUserService.page(new Page<>(pageNum, pageSize));
    //    //2.返回数据, 查询分页查询的数据结构
    //    return Result.success(page);
    //}

    /**
     * @param pageNum  显示第几页, 默认为1
     * @param pageSize 每页有多少条, 默认为10条
     * @param search   前端传来的查询条件, 默认为"", 不带条件
     * @return
     */
    //@GetMapping("listByCondition")
    //public Result listByCondition(@RequestParam(defaultValue = "1") Integer pageNum,
    //                              @RequestParam(defaultValue = "10") Integer pageSize,
    //                              SysUser sysUser) {
    //
    //    //1.先床啊进QueryWrapper, 可以将我们的检索条件封装进QueryWrapper
    //    QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
    //    // 根据用户名模糊查询
    //    if (StringUtils.hasText(sysUser.getUsername())) {
    //        wrapper.like("username", sysUser.getUsername());
    //    }
    //    //根据用户id进行查询
    //
    //    Page<SysUser> page = sysUserService.page(new Page<>(pageNum, pageSize), wrapper);
    //    return Result.success(page);
    //}

    //编写方法，使用lambdaQueryWrapper封装查询条件, 完成检索
    @ApiOperation("分页查询")
    @GetMapping("listByConditionLambda")
    public Result listByConditionLambda(@RequestParam(defaultValue = "1") Integer pageNum,
                                   @RequestParam(defaultValue = "10") Integer pageSize,
                                   SysUser sysUser) {

        //说明: 关于lambda表达式, 我们这里使用的是 类名::实例方法
        //是lambda方法引用中不太好理解的

        //解读
        //1. SysUser::getUsername 通过lambda表达式 引用实例方法 getUsername
        //2. 这里就是把 SysUser::getUsername 赋给 SFunction<T,R> 函数式接口
        //3. 查看 SFunction<T,R> 源代码
        /**
         * @FunctionalInterface
         * public interface SFunction<T, R> extends Function<T, R>, Serializable {
         * }
         * 父接口
         *@FunctionalInterface
         * public interface Function<T, R> {
         *      R apply(T t);//抽象方法, 表示根据类型T的参数, 获取类型R的结果
         *
         *      //这里有默认实现方法
         * }
         */
        //4.传入 SysUser::getUsername 后, 就相当于实现了 SFunction<T,R> 的apply方法
        //5.底层会根据 传入的 SysUser:getUsername 去得到该方法的对应的属性映射的表的字段, 更加灵活
        //<resultMap id="BaseResultMap" type="com.zzw.springboot.bean.SysMenu">
        //        <id column="id" jdbcType="INTEGER" property="id"/>

        //创建lambdaQueryWrapper, 封装查询条件
        LambdaQueryWrapper<SysUser> lambdaQueryWrapper = Wrappers.<SysUser>lambdaQuery();

        //判断
        if (StringUtils.hasText(sysUser.getUsername())) {
            //lambdaQueryWrapper.like(SysUser::getUsername, sysUser.getUsername());
            //换一个写法, 依然正确
            SFunction<SysUser, Object> sf = SysUser::getUsername;
            lambdaQueryWrapper.likeRight(sf, sysUser.getUsername());
        }
        //根据用户id进行查询
        if (sysUser.getId() != null) {
            SFunction<SysUser, Integer> sf = SysUser::getId;
            lambdaQueryWrapper.eq(sf, sysUser.getId());
        }
        if (sysUser.getStatus() != null) {
            SFunction<SysUser, Integer> sf = SysUser::getStatus;
            lambdaQueryWrapper.eq(sf, sysUser.getStatus());
        }

        Page<SysUser> page = sysUserService.page(new Page<SysUser>(pageNum, pageSize), lambdaQueryWrapper);
        log.info("page={}", page);
        return Result.success(page);
    }

    //@ApiOperation("分页查询")
    //@GetMapping("listByConditionLambda")
    //public Result listByConditionLambda(@RequestParam(required = false) Integer lastId,
    //                                    @RequestParam(defaultValue = "10") Integer pageSize,
    //                                    SysUser sysUser) {
    //
    //    //说明: 关于lambda表达式, 我们这里使用的是 类名::实例方法
    //    //是lambda方法引用中不太好理解的
    //
    //    //解读
    //    //1. SysUser::getUsername 通过lambda表达式 引用实例方法 getUsername
    //    //2. 这里就是把 SysUser::getUsername 赋给 SFunction<T,R> 函数式接口
    //    //3. 查看 SFunction<T,R> 源代码
    //    /**
    //     * @FunctionalInterface
    //     * public interface SFunction<T, R> extends Function<T, R>, Serializable {
    //     * }
    //     * 父接口
    //     *@FunctionalInterface
    //     * public interface Function<T, R> {
    //     *      R apply(T t);//抽象方法, 表示根据类型T的参数, 获取类型R的结果
    //     *
    //     *      //这里有默认实现方法
    //     * }
    //     */
    //    //4.传入 SysUser::getUsername 后, 就相当于实现了 SFunction<T,R> 的apply方法
    //    //5.底层会根据 传入的 SysUser:getUsername 去得到该方法的对应的属性映射的表的字段, 更加灵活
    //    //<resultMap id="BaseResultMap" type="com.zzw.springboot.bean.SysMenu">
    //    //        <id column="id" jdbcType="INTEGER" property="id"/>
    //
    //    //创建lambdaQueryWrapper, 封装查询条件
    //    LambdaQueryWrapper<SysUser> lambdaQueryWrapper = Wrappers.<SysUser>lambdaQuery();
    //
    //    //判断
    //    if (StringUtils.hasText(sysUser.getUsername())) {
    //        //lambdaQueryWrapper.like(SysUser::getUsername, sysUser.getUsername());
    //        //换一个写法, 依然正确
    //        SFunction<SysUser, Object> sf = SysUser::getUsername;
    //        lambdaQueryWrapper.likeRight(sf, sysUser.getUsername());
    //    }
    //    //根据用户id进行查询
    //    if (sysUser.getId() != null) {
    //        SFunction<SysUser, Integer> sf = SysUser::getId;
    //        lambdaQueryWrapper.eq(sf, sysUser.getId());
    //    }
    //    if (sysUser.getStatus() != null) {
    //        SFunction<SysUser, Integer> sf = SysUser::getStatus;
    //        lambdaQueryWrapper.eq(sf, sysUser.getStatus());
    //    }
    //
    //    Page<SysUser> page = null;
    //    lambdaQueryWrapper.orderByAsc(SysUser::getId);
    //    if (lastId == null) {
    //        page = sysUserService.page(new Page<SysUser>(0, pageSize), lambdaQueryWrapper);
    //    } else {
    //        lambdaQueryWrapper.gt(SysUser::getId, lastId);
    //        page = sysUserService.page(new Page<SysUser>(0, pageSize), lambdaQueryWrapper);
    //    }
    //    log.info("page={}", page);
    //    return Result.success(page);
    //}

    //编写方法, 根据id查询用户信息
    //使用url占位符, @PathVariable
    @ApiOperation("根据id查询")
    @GetMapping("/{id}")
    @Log(module = "用户管理", type = "查询", description = "获取用户详情")
    public Result getInfo(@PathVariable("id") Integer id) {
        // 增加用户访问计数
        String viewCountKey = USER_VIEW_COUNT_PREFIX + id;
        redisUtil.incr(viewCountKey, 1);

        // 将用户ID添加到热门用户集合
        redisUtil.sSet(HOT_USERS_KEY, id.toString());

        // 使用用户行为分析工具记录用户访问
        behaviorUtil.recordUserView(id, null);
        // 记录用户行为（查看详情）
        behaviorUtil.recordUserAction("view", id, null);

        SysUser sysUser = sysUserService.getById(id);
        log.info("sysUser={}", sysUser);
        return Result.success(sysUser);//返回成功信息, 并携带查询到的用户数据
    }

    /**
     * 获取用户访问次数
     */
    @ApiOperation("获取用户访问次数")
    @GetMapping("/viewCount/{id}")
    public Result getViewCount(@PathVariable("id") Integer id) {
        // 使用用户行为分析工具获取用户热度
        long dailyHeat = behaviorUtil.getUserDailyHeat(id);
        long monthlyHeat = behaviorUtil.getUserMonthlyHeat(id);

        Map<String, Object> result = new HashMap<>();
        result.put("dailyHeat", dailyHeat);
        result.put("monthlyHeat", monthlyHeat);

        // 兼容旧数据
        String viewCountKey = USER_VIEW_COUNT_PREFIX + id;
        Object count = redisUtil.get(viewCountKey);
        result.put("totalCount", count != null ? count : 0);

        return Result.success(result);
    }

    /**
     * 获取热门用户列表（访问次数最多的前5名）
     */
    @ApiOperation("获取热门用户列表")
    @GetMapping("/hotUsers")
    public Result getHotUsers() {
        // 使用排行榜工具获取热门用户
        List<Map<String, Object>> hotUsersRanking = behaviorUtil.getHotUsersRanking(5);

        // 如果没有排行数据，则使用旧方式获取
        if (hotUsersRanking == null || hotUsersRanking.isEmpty()) {
            return getHotUsersByOldWay();
        }

        // 提取用户ID列表
        List<Integer> hotUserIds = new ArrayList<>();
        for (Map<String, Object> item : hotUsersRanking) {
            Object userId = item.get("member");
            if (userId != null) {
                hotUserIds.add(Integer.valueOf(userId.toString()));
            }
        }

        if (hotUserIds.isEmpty()) {
            return Result.success(Collections.emptyList());
        }

        // 查询这些用户的详细信息
        List<SysUser> hotUsers = (List<SysUser>) sysUserService.listByIds(hotUserIds);

        // 构建返回结果，包含排名和分数信息
        List<Map<String, Object>> result = new ArrayList<>();
        for (int i = 0; i < hotUsers.size(); i++) {
            SysUser user = hotUsers.get(i);
            Map<String, Object> userMap = new HashMap<>();
            userMap.put("user", user);

            // 查找对应的排名信息
            for (Map<String, Object> rankInfo : hotUsersRanking) {
                if (rankInfo.get("member").toString().equals(user.getId().toString())) {
                    userMap.put("rank", rankInfo.get("rank"));
                    userMap.put("score", rankInfo.get("score"));
                    break;
                }
            }

            result.add(userMap);
        }

        return Result.success(result);
    }

    /**
     * 使用旧方式获取热门用户
     */
    private Result getHotUsersByOldWay() {
        // 获取所有热门用户ID
        Set<Object> userIds = redisUtil.sGet(HOT_USERS_KEY);
        if (userIds == null || userIds.isEmpty()) {
            return Result.success(Collections.emptyList());
        }

        // 构建热门用户Map，按访问次数排序
        Map<Integer, Long> userCountMap = new HashMap<>();
        for (Object userId : userIds) {
            Integer id = Integer.valueOf(userId.toString());
            String viewCountKey = USER_VIEW_COUNT_PREFIX + id;
            Object countObj = redisUtil.get(viewCountKey);
            if (countObj != null) {
                Long count = Long.valueOf(countObj.toString());
                userCountMap.put(id, count);
            }
        }

        // 按访问次数排序
        List<Map.Entry<Integer, Long>> sortedUsers = new ArrayList<>(userCountMap.entrySet());
        sortedUsers.sort(Map.Entry.comparingByValue(Comparator.reverseOrder()));

        // 获取前5名用户ID
        List<Integer> hotUserIds = new ArrayList<>();
        for (int i = 0; i < Math.min(5, sortedUsers.size()); i++) {
            hotUserIds.add(sortedUsers.get(i).getKey());
        }

        if (hotUserIds.isEmpty()) {
            return Result.success(Collections.emptyList());
        }

        // 查询这些用户的详细信息
        List<SysUser> hotUsers = (List<SysUser>) sysUserService.listByIds(hotUserIds);

        return Result.success(hotUsers);
    }

    //编写方法, 完成数据添加
    //前端如果以json格式提交数据, 我们需要用@RequestBody注解将数据封装到javabean中
    //如果是以表单的方式提交,不需要@RequestBody注解, 才进行数据封装
    @ApiOperation("用户新增")
    @PostMapping("/save")
    @Log(module = "用户管理", type = "新增", description = "新增用户信息")
    public Result save(@RequestBody @Validated SysUser sysUser, Errors errors) {
        Map<String, Object> map = validateForm(errors);
        if (!map.isEmpty()) {
            return Result.error(map, "数据校验失败");
        }
        boolean save = sysUserService.save(sysUser);
        if (save) {
            return Result.success("添加成功");
        }
        return Result.error("添加失败");
    }

    @ApiOperation("用户更新")
    @PutMapping("/update")
    @Log(module = "用户管理", type = "更新", description = "更新用户信息")
    public Result update(@RequestBody SysUser sysUser) {
        boolean update = sysUserService.updateById(sysUser);
        if (update) {
            return Result.success("修改成功");
        }
        return Result.error("修改失败");
    }

    @ApiOperation("删除用户")
    @DeleteMapping("/{id}")
    @Log(module = "用户管理", type = "删除", description = "删除用户信息")
    public Result delete(@PathVariable("id") Integer id) {
        boolean delete = sysUserService.removeById(id);
        if (delete) {
            return Result.success("删除成功");
        }
        return Result.error("删除失败");
    }

    /**
     * 验证表单
     */
    private Map<String, Object> validateForm(Errors errors) {
        Map<String, Object> errorMap = new HashMap<>();
        List<FieldError> fieldErrors = errors.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return errorMap;
    }

    /**
     * 修改用户密码
     */
    @ApiOperation("修改密码")
    @PutMapping("changePassword")
    public Result changePassword(@RequestParam Integer id, @RequestParam String newPassword) {
        if (sysUserService.changePassword(id, newPassword)) {
            return Result.success("修改成功");
        }
        return Result.error("修改失败");
    }

    /**
     * 状态修改
     */
    @ApiOperation("状态修改")
    @PutMapping("changeStatus")
    public Result changeStatus(@RequestBody SysUser sysUser) {
        if (sysUserService.changeStatus(sysUser)) {
            return Result.success("修改成功");
        }
        return Result.error("修改失败");
    }

    /**
     * 重置密码
     */
    @ApiOperation("重置密码")
    @PutMapping("resetPwd")
    public Result resetPwd(@RequestBody SysUser sysUser) {
        if (sysUserService.resetPwd(sysUser)) {
            return Result.success("重置成功");
        }
        return Result.error("重置失败");
    }

    /**
     * 批量删除
     */
    @ApiOperation("批量删除")
    @DeleteMapping("batchDelete")
    public Result batchDelete(@RequestBody List<Integer> ids) {
        boolean b = sysUserService.removeByIds(ids);
        if (b) {
            return Result.success("删除成功");
        }
        return Result.error("删除失败");
    }

    /**
     * 用户数据导出
     */
    @ApiOperation("导出用户")
    @PostMapping("/export")
    public void export(@RequestBody List<Integer> ids, HttpServletResponse response) throws IOException {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode("用户列表", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        // 获取所有用户
        //创建lambdaQueryWrapper, 封装查询条件
        LambdaQueryWrapper<SysUser> lambdaQueryWrapper = Wrappers.<SysUser>lambdaQuery();

        //根据用户ids进行查询
        if (ids != null && !ids.isEmpty()) {
            SFunction<SysUser, Integer> sf = SysUser::getId;
            lambdaQueryWrapper.in(sf, ids);
        }
        List<SysUser> userList = sysUserService.list(lambdaQueryWrapper);
        List<UserExcelVO> excelDataList = new ArrayList<>();

        // 转换为Excel数据对象
        for (SysUser user : userList) {
            UserExcelVO excelVO = new UserExcelVO();
            BeanUtils.copyProperties(user, excelVO);
            // 处理特殊字段
            excelVO.setUserStatus(user.getStatus() == 1 ? "正常" : "停用");
            excelVO.setGenderDesc(user.getGender() == 1 ? "男" : "女");
            excelDataList.add(excelVO);
        }

        // 使用EasyExcel导出
        EasyExcel.write(response.getOutputStream(), UserExcelVO.class)
                .sheet("用户列表")
                .doWrite(excelDataList);
    }

    /**
     * 导入用户/下载模版
     */
    @ApiOperation("下载模版")
    @GetMapping("downloadTemplate")
    public void downloadTemplate(HttpServletResponse response) throws IOException {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode("用户导入模板", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        // 创建示例数据
        List<UserExcelVO> list = new ArrayList<>();
        UserExcelVO demo = new UserExcelVO();
        demo.setUsername("示例用户名");
        demo.setPassword("示例密码");
        demo.setEmail("example@example.com");
        demo.setMobilePhone("13812345678");
        demo.setUserStatus("正常");
        demo.setNickname("示例昵称");
        demo.setRealName("示例姓名");
        demo.setGenderDesc("男");
        list.add(demo);

        // 使用EasyExcel导出模板
        EasyExcel.write(response.getOutputStream(), UserExcelVO.class)
                .sheet("用户导入模板")
                .doWrite(list);
    }

    /**
     * 导入用户
     */
    @ApiOperation("导入用户")
    @PostMapping("/import")
    public Result importUser(@RequestParam("file") MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            return Result.error("请选择要上传的文件");
        }

        // 存储导入的用户数据
        List<SysUser> userList = new ArrayList<>();

        // 使用EasyExcel读取
        EasyExcel.read(file.getInputStream(), UserExcelVO.class, new AnalysisEventListener<UserExcelVO>() {
            @Override
            public void invoke(UserExcelVO data, AnalysisContext context) {
                // 排除表头行
                if (data.getUsername() != null && !data.getUsername().equals("示例用户名")) {
                    SysUser user = new SysUser();
                    BeanUtils.copyProperties(data, user);
                    // 处理特殊字段
                    user.setStatus("正常".equals(data.getUserStatus()) ? 1 : 0);
                    user.setGender("男".equals(data.getGenderDesc()) ? 1 : 0);
                    userList.add(user);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                log.info("用户数据导入完成，共导入{}条记录", userList.size());
            }
        }).sheet().doRead();

        // 批量保存用户
        if (!userList.isEmpty()) {
            boolean success = sysUserService.saveBatch(userList);
            if (success) {
                return Result.success("成功导入" + userList.size() + "条用户数据");
            }
        }

        return Result.error("导入失败");
    }

    /**
     * 上传用户头像
     */
    @ApiOperation("上传用户头像")
    @PostMapping("uploadAvatar")
    public Result changeAvatar(@RequestParam("avatar") MultipartFile avatar,
                               @RequestParam("id") Integer id) throws IOException {
        SysUser originalUser = sysUserService.getById(id);
        if (originalUser == null) {
            return Result.error("用户不存在");
        }
        if (avatar.isEmpty()) {
            return Result.error("上传文件为空");
        }

        //存储到类路径(运行的时候) /target/classes/
        String classPath = ResourceUtils.getURL("classpath:").getPath();
        String avatarPath = "/static/image/avatar/";

        String destPath = classPath + avatarPath;
        File file = new File(destPath);
        if (!file.exists()) {
            file.mkdirs();
        }

        String originalFilename = avatar.getOriginalFilename();
        avatar.transferTo(new File(destPath, originalFilename));

        //用户头像地址url,保存到数据库
        avatarPath = "/api/image/avatar/" + originalFilename;
        originalUser.setAvatar(avatarPath);
        if (!sysUserService.updateById(originalUser)) {
            return Result.error("上传失败");
        }
        return Result.success(avatarPath);
    }

    /**
     * Excel导入导出用的VO类
     */
    @Data
    public static class UserExcelVO {
        @ExcelProperty("用户名")
        private String username;

        @ExcelProperty("密码")
        private String password;

        @ExcelProperty("邮箱")
        private String email;

        @ExcelProperty("手机号")
        private String mobilePhone;

        @ExcelProperty("用户状态")
        private String userStatus;

        @ExcelProperty("用户昵称")
        private String nickname;

        @ExcelProperty("真实姓名")
        private String realName;

        @ExcelProperty("性别")
        private String genderDesc;

        @ExcelProperty("备注")
        private String remark;
    }
}
